package cluster

import (
	"gitee.com/then-go-project/go-utils/gredis"
	"github.com/go-redis/redis"
	"time"
)

type client struct {
	client *redis.ClusterClient
}

func (c *client) Execute(cmd string, args ...interface{}) (interface{}, error) {
	var newCmd []interface{}
	newCmd = append(newCmd, cmd)
	newCmd = append(newCmd, args...)
	return c.client.Do(newCmd...).Result()
}

func (c *client) Set(key string, data interface{}, timeoutSeconds int) error {
	return c.client.Set(key, data, time.Duration(timeoutSeconds)*time.Second).Err()
}

func (c *client) Get(key string, result interface{}) error {
	return c.client.Get(key).Scan(result)
}

func (c *client) Delete(keys ...string) error {
	return c.client.Del(keys...).Err()
}

func (c *client) Exists(key string) (bool, error) {
	if result, err := c.client.Exists(key).Result(); err != nil {
		return false, err
	} else {
		return result == 1, nil
	}
}

func (c *client) Expire(key string, timeoutSeconds int) error {
	return c.client.Expire(key, time.Duration(timeoutSeconds)*time.Second).Err()
}

func (c *client) GetExpire(key string) (int64, error) {
	result, err := c.Execute("TTL", key)
	c.client.PFCount()
	if err != nil {
		return -3, err
	}
	return result.(int64), nil
}

func (c *client) SetHash(key, hKey string, data interface{}, timeoutSeconds int) error {
	pipeline := c.client.Pipeline()
	pipeline.HSet(key, hKey, data)
	pipeline.Expire(key, time.Duration(timeoutSeconds)*time.Second)
	_, err := pipeline.Exec()
	return err
}

func (c *client) GetHash(key, hKey string, result interface{}) error {
	return c.client.HGet(key, hKey).Scan(result)
}

func (c *client) DeleteHash(key, hKey string) error {
	return c.client.HDel(key, hKey).Err()
}

func (c *client) GetHashAll(key string) ([]interface{}, error) {
	return c.client.HMGet(key).Result()
}

func (c *client) GetLock(key, token string, timeoutSeconds int) (bool, error) {
	return c.client.SetNX(key, token, time.Duration(timeoutSeconds)*time.Second).Result()
}

func (c *client) UnLock(key, token string) (bool, error) {
	script := redis.NewScript(` if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
`)
	result, err := script.Run(c.client, []string{key}, token).Int()
	if err != nil || result == 0 {
		return false, err
	}
	return true, err
}

func (c *client) String() string {
	return "cluster"
}

func GenerateClient(addr []string, optFunc ...OptFunc) gredis.BaseRedisClient {
	clusterOptions := redis.ClusterOptions{
		Addrs: addr,
	}
	for _, opt := range optFunc {
		opt(&clusterOptions)
	}
	c := redis.NewClusterClient(&clusterOptions)
	return &client{
		client: c,
	}
}
